//
//  PGSecKey.m
//  iPWManager
//
//  Created by Alexander Wahl on 11/10/11.
//  Copyright (c) 2011 __MyCompanyName__. All rights reserved.
//

#import "PGSecKey.h"

@implementation PGSecKey

@synthesize callbackID,privateTag,publicTag;


-(void)getBase64EncodePrivateKey:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options  {
    
    //The first argument in the arguments parameter is the callbackID.
    //We use this to send data back to the successCallback or failureCallback
    //through PluginResult.   
    self.callbackID = [arguments pop];

    
    NSString * privateKey =  [self readFromTextFile:@"private_key.pem"];
   
    if ( privateKey != nil) {
        
       

        //Create Plugin Result
        PluginResult* pluginResult = [PluginResult resultWithStatus:PGCommandStatus_OK messageAsString:privateKey];
        
        [self writeJavascript: [pluginResult toSuccessCallbackString:self.callbackID]];
    } else {  
        PluginResult* pluginResult = [PluginResult resultWithStatus:PGCommandStatus_OK messageAsString:@"Private Key is null"];
        //Call  the Failure Javascript function
        [self writeJavascript: [pluginResult  toErrorCallbackString:self.callbackID]];
    }
    
}

- (void) getBase64EncodePublicKey:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
    //The first argument in the arguments parameter is the callbackID.
    //We use this to send data back to the successCallback or failureCallback
    //through PluginResult.   
    self.callbackID = [arguments pop];

    
    NSString * publicKey = [self readFromTextFile:@"public_key.pem"];
    
    if ( publicKey != nil) {
        //Create Plugin Result
        PluginResult* pluginResult = [PluginResult resultWithStatus:PGCommandStatus_OK messageAsString:publicKey];
        
        [self writeJavascript: [pluginResult toSuccessCallbackString:self.callbackID]];
    } else {
        PluginResult* pluginResult = [PluginResult resultWithStatus:PGCommandStatus_OK messageAsString:@"Public Key is null"];
        //Call  the Failure Javascript function
        [self writeJavascript: [pluginResult  toErrorCallbackString:self.callbackID]];
    }
    
}
- (void) deleteKeys:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
    //The first argument in the arguments parameter is the callbackID.
    //We use this to send data back to the successCallback or failureCallback
    //through PluginResult.   
    self.callbackID = [arguments pop];
    NSString *privateKeyTag = [arguments objectAtIndex:0];
    NSString *publicKeyTag = [arguments objectAtIndex:1];    
    privateTag = [privateKeyTag dataUsingEncoding: NSASCIIStringEncoding];
    publicTag = [publicKeyTag dataUsingEncoding: NSASCIIStringEncoding];
    
    //[self deleteAsymmetricKeys];
    
    if ( [self deleteFile:@"private_key.pem"] == true 
        && [self deleteFile:@"public_key.pem"] == true ) {
        
        PluginResult* pluginResult = [PluginResult resultWithStatus:PGCommandStatus_OK messageAsString:@"Delete Key success"];
        //Call  the Failure Javascript function
        [self writeJavascript: [pluginResult  toSuccessCallbackString:self.callbackID]];
        
    } else {
        PluginResult* pluginResult = [PluginResult resultWithStatus:PGCommandStatus_OK messageAsString:@"Delete Key fail"];
        //Call  the Failure Javascript function
        [self writeJavascript: [pluginResult  toErrorCallbackString:self.callbackID]];
    }
    
}
- (void) generateKeys:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
    
    //The first argument in the arguments parameter is the callbackID.
    //We use this to send data back to the successCallback or failureCallback
    //through PluginResult.   
    self.callbackID = [arguments pop];
    
    NSString *privateKeyTag = [arguments objectAtIndex:0];
    NSString *publicKeyTag = [arguments objectAtIndex:1];    
    privateTag = [privateKeyTag dataUsingEncoding: NSASCIIStringEncoding];
    publicTag = [publicKeyTag dataUsingEncoding: NSASCIIStringEncoding];
    
    [self generateKeys];

    PluginResult* pluginResult = [PluginResult resultWithStatus:PGCommandStatus_OK messageAsString:@"Generate Key success"];
    //Call  the Failure Javascript function
    [self writeJavascript: [pluginResult  toSuccessCallbackString:self.callbackID]];

}

- (void) generateKeys {
    SecKeyRef publicKey = NULL;
    SecKeyRef privateKey = NULL;
    OSStatus sanityCheck = noErr;
    NSUInteger keySize = 2048;
    
	
	// Container dictionaries.
	NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init];
	NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init];
	NSMutableDictionary * keyPairAttr = [[NSMutableDictionary alloc] init];
	
	// Set top level dictionary for the keypair.
	[keyPairAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
	[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(id)kSecAttrKeySizeInBits];
	
	// Set the private key dictionary.
	[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
    
    [privateKeyAttr setObject:privateTag forKey:(id)kSecAttrApplicationTag];
	// See SecKey.h to set other flag values.
	
	// Set the public key dictionary.
	[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
	[publicKeyAttr setObject:publicTag forKey:(id)kSecAttrApplicationTag];
	// See SecKey.h to set other flag values.
	
	// Set attributes to top level dictionary.
	[keyPairAttr setObject:privateKeyAttr forKey:(id)kSecPrivateKeyAttrs];
	[keyPairAttr setObject:publicKeyAttr forKey:(id)kSecPublicKeyAttrs];
	
	// SecKeyGeneratePair returns the SecKeyRefs just for educational purposes.
	sanityCheck = SecKeyGeneratePair((CFDictionaryRef)keyPairAttr, &publicKey, &privateKey);
    
    NSData * privateKeyBits = [self getPrivateKeyBits];
    NSString * privateKeyString =  @"-----BEGIN RSA PRIVATE KEY-----\n";
    privateKeyString = [privateKeyString stringByAppendingString:[privateKeyBits base64EncodingWithLineLength:64]];
    privateKeyString = [privateKeyString stringByAppendingString:@"\n-----END RSA PRIVATE KEY-----\n"];
    
    [self writeToTextFile:privateKeyString fileName:@"private_key.pem"];
    
    NSString * publicKeyString = [self getRSAPublicKeyAsBase64];
    [self writeToTextFile:publicKeyString fileName:@"public_key.pem"];
    
    [self deleteAsymmetricKeys];
    
    [privateKeyAttr release];
    [publicKeyAttr release];
    [keyPairAttr release];
}

- (NSData *)getPrivateKeyBits {
    OSStatus sanityCheck = noErr;
    NSData * privateKeyBits = nil;
    NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init];
    
    // Set the private key query dictionary.
    [queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
    [queryPrivateKey setObject:privateTag forKey:(id)kSecAttrApplicationTag];
    [queryPrivateKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
    [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
    
    // Get the key bits.
    sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyBits);
    
    if (sanityCheck != noErr) {
        privateKeyBits = nil;
    }
    
    [queryPrivateKey release];
    
    return privateKeyBits;
}

- (NSData *)getPublicKeyBits {
	OSStatus sanityCheck = noErr;
	NSData * publicKeyBits = nil;
	
	NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
    
	// Set the public key query dictionary.
	[queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
	[queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag];
	[queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
	[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
    
	// Get the key bits.
	sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBits);
    
	if (sanityCheck != noErr) {
		publicKeyBits = nil;
	}
    
	[queryPublicKey release];
	
	return publicKeyBits;
}

- (void)deleteAsymmetricKeys {
	OSStatus sanityCheck = noErr;
	NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
	NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init];
	
	// Set the public key query dictionary.
	[queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
	[queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag];
	[queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
	
	// Set the private key query dictionary.
	[queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
	[queryPrivateKey setObject:privateTag forKey:(id)kSecAttrApplicationTag];
	[queryPrivateKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
	
	// Delete the private key.
	sanityCheck = SecItemDelete((CFDictionaryRef)queryPrivateKey);
    
	// Delete the public key.
	sanityCheck = SecItemDelete((CFDictionaryRef)queryPublicKey);
    
	[queryPrivateKey release];
	[queryPublicKey release];
}

- (NSString *) getRSAPublicKeyAsBase64 {
    
    static const unsigned char _encodedRSAEncryptionOID[15] = {
        
        /* Sequence of length 0xd made up of OID followed by NULL */
        0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
        0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00
        
    };
 
    NSData * publicKeyBits = [self getPublicKeyBits];
    NSString * publicKey = NULL;
    
    if (publicKeyBits != nil) {
        // OK - that gives us the "BITSTRING component of a full DER
        // encoded RSA public key - we now need to build the rest
        
        unsigned char builder[15];
        NSMutableData * encKey = [[NSMutableData alloc] init];
        int bitstringEncLength;
        
        // When we get to the bitstring - how will we encode it?
        if  ([publicKeyBits length ] + 1  < 128 ) {
            bitstringEncLength = 1 ;
        } else {
            bitstringEncLength = (([publicKeyBits length ] +1 ) / 256 ) + 2 ; 
        }
        
        // Overall we have a sequence of a certain length
        builder[0] = 0x30;    // ASN.1 encoding representing a SEQUENCE
        // Build up overall size made up of -
        // size of OID + size of bitstring encoding + size of actual key
        size_t i = sizeof(_encodedRSAEncryptionOID) + 2 + bitstringEncLength + [publicKeyBits length];
        size_t j = [self encodeLength:&builder[1] withLenght:i];
        //encodeLength(&builder[1], i);
        [encKey appendBytes:builder length:j +1];
        
        // First part of the sequence is the OID
        [encKey appendBytes:_encodedRSAEncryptionOID length:sizeof(_encodedRSAEncryptionOID)];
        
        // Now add the bitstring
        builder[0] = 0x03;
        j = [self encodeLength:&builder[1] withLenght:[publicKeyBits length] + 1];
        // j = encodeLength(&builder[1], [publicKeyBits length] + 1);
        builder[j+1] = 0x00;
        [encKey appendBytes:builder length:j + 2];
        
        // Now the actual key
        [encKey appendData:publicKeyBits];
        
        // Now translate the result to a Base64 string
        publicKey = @"ssh-rsa";
        publicKey = @"-----BEGIN PUBLIC KEY-----\n";
        publicKey = [publicKey stringByAppendingString:[encKey base64EncodingWithLineLength:64]];
        publicKey = [publicKey stringByAppendingString:@"\n-----END PUBLIC KEY-----\n"];
        
        [encKey release];
    } 
    
    return publicKey;
}

// Helper function for ASN.1 encoding
- (size_t) encodeLength: (unsigned char *) buf withLenght:(size_t) length {
 
    // encode length in ASN.1 DER format
    if (length < 128) {
        buf[0] = length;
        return 1;
    }
    
    size_t i = (length / 256) + 1;
    buf[0] = i + 0x80;
    for (size_t j = 0 ; j < i; ++j) {         buf[i - j] = length & 0xFF;         length = length >> 8;
    }
    
    return i + 1;   
}

- (void) writeToTextFile: (NSString *) content fileName: (NSString*) fileName{
    //get the documents directory:
    NSArray *paths = NSSearchPathForDirectoriesInDomains
    (NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    
    //make a file name to write the data to using the documents directory:
    NSString *fName = [NSString stringWithFormat:@"%@/%@", 
                       documentsDirectory,fileName];

    //save content to the documents directory
    [content writeToFile:fName 
              atomically:NO 
                encoding:NSStringEncodingConversionAllowLossy 
                   error:nil];
    
}
- (NSString *) readFromTextFile: (NSString*) fileName {
    //get the documents directory:
    NSArray *paths = NSSearchPathForDirectoriesInDomains
    (NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    
    //make a file name to write the data to using the documents directory:
    NSString *fName = [NSString stringWithFormat:@"%@/%@", 
                          documentsDirectory,fileName];
    NSString *content = [[NSString alloc] initWithContentsOfFile:fName
                                                    usedEncoding:nil
                                                           error:nil];


    return content;
    [content release];
    
}

- (Boolean) deleteFile: (NSString*) fileName {
    Boolean success = true;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    //get the documents directory:
    NSArray *paths = NSSearchPathForDirectoriesInDomains
    (NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    
    //make a file name to write the data to using the documents directory:
    NSString *fName = [NSString stringWithFormat:@"%@/%@", 
                       documentsDirectory,fileName];
    
    if ([fileManager removeItemAtPath:fName error:NULL] != YES)
        success = false;
    
    return success;
}
@end
